package server.tateti;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.PrintStream;

import model.Usuario;

/**
 * Esta clase representa a un juego de tateti. Permite que se realice una
 * partida de este juego
 * 
 * @author romeroy
 * 
 */
public class Tateti {
	// Constantes
	// -------------------------------------------------------
	private static final String INPUT_TEST_PATH = "test/tateti/input/";
	private static final String OUTPUT_TEST_PATH = "test/tateti/outputGenerado/";

	/**
	 * Identificador del jugador 1
	 */
	public static final Integer J1 = 0;
	/**
	 * Identificador de jugador 2
	 */
	public static final Integer J2 = 1;
	public static final Integer EMPATE = -2;
	public static final Integer NINGUN_JUGADOR = -1;

	// Atributos
	// -------------------------------------------------------
	/**
	 * Identificador de juego
	 */
	private Integer id;
	/**
	 * Indica de quien es el turno de jugar
	 */
	private Integer turno;
	/**
	 * Almacena los jugadores
	 */
	private Jugador[] jugadores;
	/**
	 * Almacena el tablero de juego
	 */
	private int[] tablero;
	/**
	 * Almacena los indices del tablero que deben ser iguales para declarar un
	 * ganador
	 */
	private int[][] formasGanar;

	/**
	 * Identifica a la partida como partida terminada
	 */
	private boolean terminada;
	
	private int cantidadJugadas;

	// Constructores
	// -------------------------------------------------------
	public Tateti() {
		/*
		 * Declaro los indices del tablero que deben ser iguales para declarar
		 * un ganador
		 */
		this.formasGanar = new int[][] { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 },
				{ 0, 3, 6 }, { 1, 4, 7 }, { 2, 5, 8 }, { 0, 4, 8 }, { 2, 4, 6 } };
		tablero = new int[9];
		inicializar();
	}

	public Tateti(int id, Usuario jugador1, Usuario jugador2) {
		this();
		this.id = id;
		this.jugadores = new Jugador[2];
		this.jugadores[J1] = new Jugador(J1, jugador1, true);
		this.jugadores[J2] = new Jugador(J2, jugador2, false);
	}

	// Getters and setters
	// -------------------------------------------------------
	public Integer getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public Integer getTurno() {
		return turno;
	}

	public Jugador[] getJugadores() {
		return jugadores;
	}

	public void setJugadores(Jugador[] jugadores) {
		this.jugadores = jugadores;
	}

	public void setTablero(int[] tablero) {
		this.tablero = tablero;
	}

	public int[] getTablero() {
		return tablero;
	}

	// Metodos
	// -------------------------------------------------------
	public void inicializar() {
		turno = J1;
		cantidadJugadas = 0;
		
		// Inicializo el tablero a cero
		for (int i = 0; i < 9; i++)
			tablero[i] = NINGUN_JUGADOR;
	}
	
	/**
	 * Pone una marca del jugador en la posicion indicada, siempre y cuando sea
	 * su turno de jugar
	 * 
	 * @param jugador
	 *            que desea marcar en el tablero
	 * @param posicion
	 *            posicion en la que se quiere marcar
	 * @return verdadero si fue posible marcar, falso en caso contrario
	 */
	public boolean marcar(int jugador, Integer posicion) {
		if (turno == jugador && tablero[posicion] == NINGUN_JUGADOR) {
			tablero[posicion] = jugador;
			turno = turno == J1 ? J2 : J1;
			cantidadJugadas++;
			return true;
		} else {
			if(turno == jugador)
				System.out.println("Tateti " + id + " - " + jugadores[jugador].getUsuario().getNick() +
					" intento marcar en una posicion ocupada");
			else	
				System.out.println("Tateti " + id + " - " + jugadores[jugador].getUsuario().getNick() +
					" intento marcar cuando no era su turno");
		}
		return false;
	}

	/**
	 * Pone una marca del jugador en la posicion indicada, siempre y cuando sea
	 * su turno de jugar
	 * 
	 * @param usuario
	 *            Usuario que desea marcar en el tablero
	 * @param posicion
	 *            posicion en la que se quiere marcar
	 * @return verdadero si fue posible marcar, falso en caso contrario
	 */
	public boolean marcar(Usuario usuario, Integer posicion) {
		if (jugadores[J1].equals(usuario))
			return marcar(J1, posicion);
		else if (jugadores[J2].equals(usuario))
			return marcar(J2, posicion);
		return false;
	}

	/**
	 * Verifica si hay ganadores en el juego
	 * 
	 * @return <li>JUGADOR_1 o JUGADOR_2 en caso de que sean alguno de los 2
	 *         ganadores</li> <li>NINGUN_JUGADOR en caso de que no haya
	 *         ganadores</li>
	 */
	public Integer verificarGanador() {
		int a, b, c;
		int length = this.formasGanar.length;
		
		if(cantidadJugadas == 9)
			return EMPATE;
		
		for (int i = 0; i < length; i++) {
			// Busco los indices de las formas de ganar
			a = this.formasGanar[i][0];
			b = this.formasGanar[i][1];
			c = this.formasGanar[i][2];

			if (tablero[a] != NINGUN_JUGADOR && tablero[a] == tablero[b]
					&& tablero[a] == tablero[c]) {
				// Tenemos ganador
				int ganador = tablero[a]; // es indistinto elegir cualquira, son
											// iguales
				terminarPartida(ganador);
				return ganador;
			}
		}
		return NINGUN_JUGADOR;
	}

	/**
	 * Termina la partida
	 */
	private void terminarPartida(int ganador) {
		int puntaje = jugadores[ganador].getPuntaje() + 1;
		jugadores[ganador].setPuntaje(puntaje);
		terminada = true;
	}

	/**
	 * Verifica si un usuario pertenece a esta partida
	 */
	public boolean esJugador(Usuario usuario) {
		return jugadores[J1].equals(usuario) || jugadores[J2].equals(usuario);
	}

	/**
	 * Obtiene el jugador en base al usuario
	 * 
	 * @param usuario
	 * @return El objeto jugador en caso positivo, null en caso que el usuario
	 *         no juege en esta partida
	 */
	public Jugador getJugador(Usuario usuario) {
		if (jugadores[J1].getUsuario().equals(usuario))
			return jugadores[J1];
		else if (jugadores[J2].getUsuario().equals(usuario))
			return jugadores[J2];
		return null;
	}

	/**
	 * Obtiene el jugador en base al numero de jugador
	 * 
	 * @param id
	 *            numero de jugador a obtener
	 * @return El objeto jugador en caso positivo, null en caso de no encontrar
	 *         ese jugador
	 */
	public Jugador getJugador(int id) {
		if (id == J1)
			return jugadores[J1];
		else if (id == J2)
			return jugadores[J2];
		return null;
	}

	/**
	 * Verifica si la partida esta terminada
	 * 
	 * @return
	 */
	public boolean partidaTerminada() {
		return terminada;
	}

	public static void main(String[] args) {
		double inicio, fin;
		int id;
		// Abro carpeta test/input
		File directorio = new File(Tateti.INPUT_TEST_PATH);
		String nombre;

		// Recorro los archivos uno a uno
		for (File archivo : directorio.listFiles()) {

			// Obtengo el nombre del archivo
			nombre = archivo.getName();
			nombre = nombre.split("\\.")[0];

			// Ejecuto la prueba
			try {
				FileReader file = new FileReader(new File(
						Tateti.INPUT_TEST_PATH + nombre + ".in"));
				BufferedReader in = new BufferedReader(file);
				PrintStream out = new PrintStream(new File(
						Tateti.OUTPUT_TEST_PATH + nombre + ".out"));

				try {
					Tateti tateti = new Tateti();
					int[] tablero = new int[9];

					String[] n = in.readLine().split(" ");
					for (int i = 0; i < n.length; i++)
						tablero[i] = Integer.parseInt(n[i]);
					tateti.setTablero(tablero);

					// Ejecuto prueba
					inicio = System.currentTimeMillis();
					id = tateti.verificarGanador();
					fin = System.currentTimeMillis();

					if (id != NINGUN_JUGADOR) {
						String ganador = id == J1 ? "JUGADOR_1" : "JUGADOR_2";
						out.println("El ganador es " + ganador);
					} else {
						out.println("No hay ganador");
					}

					// Muestro tiempo utilizado
					System.out.println(nombre + ": " + (fin - inicio) + " ms");

				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					file.close();
					out.close();
				}

			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

}
